package com.hitqz.robot.camera;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.XML;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson.JSONArray;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import com.hitqz.robot.api.business.enums.BrandEnum;
import com.hitqz.robot.camera.callback.HkTemperatureCallBack;
import com.hitqz.robot.camera.configure.CameraProperties;
import com.hitqz.robot.camera.configure.EquipmentCmdEnum;
import com.hitqz.robot.camera.dto.*;
import com.hitqz.robot.camera.entity.ConstantsFace;
import com.hitqz.robot.camera.entity.HkCameraDriver;
import com.hitqz.robot.camera.entity.TemplateArea;
import com.hitqz.robot.camera.exception.EquipmentException;
import com.hitqz.robot.camera.service.HCNetSDK;
import com.hitqz.robot.camera.service.PlayCtrl;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 海康-云台-相机控制
 * @author xupkun
 * @date 2024/11/2
 */
@Service("HikVisionService")
@Slf4j
public class HikVisionService implements IEquipmentService, CommandLineRunner {


    private static HCNetSDK hcNetSDK;

    private final SAXReader saxReader = new SAXReader();

    private final Table<String, String, String> paramsCacheTable = HashBasedTable.create();

    @Resource
    private CameraProperties properties;

    private final String rootDir = System.getProperty("user.dir");

    private static final Cache<String, Boolean> stopCache = CacheBuilder.newBuilder()
            .maximumSize(1)
            .expireAfterWrite(2, TimeUnit.SECONDS) // 缓存过期时间
            .build();

    //去重的set
    private static final Set<String> retrySet = new HashSet<>();


    @Override
    public BrandEnum brand() {
        return BrandEnum.HK;
    }

    @Override
    public boolean intSdk() {
        boolean result = true;
        if (ConstantsFace.HK_NET_SDK == null) {
            if (!createSDKInstance()) {
                log.error("[HK]netSdk初始化失败----------");
                result = false;
            } else {
                log.info("[HK]netSdk载入成功----------");
                hcNetSDK = ConstantsFace.HK_NET_SDK;
                //初始设置
                hcNetSDK.NET_DVR_Init();
                hcNetSDK.NET_DVR_SetConnectTime(2000, 3);
                hcNetSDK.NET_DVR_SetReconnect(10000, true);
                String path = Optional.ofNullable(properties.getHk().getLogPath()).orElse(rootDir) + File.separator + "sdkLog" + File.separator;
                hcNetSDK.NET_DVR_SetLogToFile(3, path, true);
            }
        }
        if (ConstantsFace.HK_PLAY_SDK == null) {
            if (createPlayInstance()) {
                log.info("[HK]playSdk载入成功----------");
            } else {
                log.error("[HK]playSdk初始化失败----------");
            }
        }
        return result;
    }


    @Override
    public boolean login(EquipmentLoginParamsDto dto) {
        boolean result = true;
        String ip = dto.getIp(), userName = dto.getUsername(), password = dto.getPassword();
        int port = dto.getPort();
        if (ConstantsFace.HK_CAMERA_DRIVER_MAP.containsKey(ip)) {
            return true;
        }
        try {
            HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
            HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();
            m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
            System.arraycopy(ip.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, ip.length());
            m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
            System.arraycopy(userName.getBytes(), 0, m_strLoginInfo.sUserName, 0, userName.length());
            m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
            System.arraycopy(password.getBytes(), 0, m_strLoginInfo.sPassword, 0, password.length());
            m_strLoginInfo.wPort = (short) port;
            m_strLoginInfo.bUseAsynLogin = false;
            m_strLoginInfo.byLoginMode = 0;//sdk登录模式
            m_strLoginInfo.write();
            int userId = hcNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
            log.info("[HK]login ip:{},userId:{},channelNum:{},startChannel:{}", ip, userId, m_strDeviceInfo.struDeviceV30.byChanNum, m_strDeviceInfo.struDeviceV30.byStartChan);
            if (userId == -1) {
                log.error("[HK]{} login error:{}", ip, hcNetSDK.NET_DVR_GetLastError());
                if (retrySet.add(ip+port)) {
                    ConstantsFace.HK_LOGIN_FAIL_LIST.add(dto);
                }
                return false;
            }
            HkCameraDriver hkCameraDriver = new HkCameraDriver(ip, port, userName, password, userId, m_strDeviceInfo.struDeviceV30.byChanNum, m_strDeviceInfo.struDeviceV30.byStartChan);
            ConstantsFace.HK_CAMERA_DRIVER_MAP.put(hkCameraDriver.getIp(), hkCameraDriver);
            SpringUtil.getApplicationContext().publishEvent(hkCameraDriver);
        } catch (Exception e) {
            log.error("[HK]{} login error:{}", ip, hcNetSDK.NET_DVR_GetLastError());
            if (retrySet.add(ip+port)) {
                ConstantsFace.HK_LOGIN_FAIL_LIST.add(dto);
            }
            result = false;
        }
        return result;
    }

    @Override
    public boolean loginout(String ip) {
        HkCameraDriver hkCameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(ip);
        hcNetSDK.NET_DVR_Logout(hkCameraDriver.getUserId());
        ConstantsFace.HK_CAMERA_DRIVER_MAP.remove(ip);
        return true;
    }

    @Override
    public void run(String... args) throws Exception {
        //jna崩溃改为抛异常
        if (SystemUtil.getOsInfo().isWindows()) {
            System.setProperty("jna.protected", "true");
        }
        log.info("os.name:{},os.version:{}", System.getProperty("os.name"), System.getProperty("os.version"));
    }


    private boolean createPlayInstance() {
        if (ConstantsFace.HK_PLAY_SDK == null) {
            synchronized (PlayCtrl.class) {
                String strPlayPath = "";
                try {
                    String libPath = properties.getHk().getLibPath();
                    if (SystemUtil.getOsInfo().isWindows()) {
                        //win系统加载库路径
                        strPlayPath = libPath + "\\PlayCtrl.dll";
                    }
                    else if (SystemUtil.getOsInfo().isLinux()) {
                        //Linux系统加载库路径
                        strPlayPath = libPath + "/libPlayCtrl.so";
                    }
                    return true;
                } catch (Exception ex) {
                    log.error("loadLibrary: " + strPlayPath + " Error: " + ex.getMessage());
                    return false;
                }
            }
        }
        return true;
    }


    private boolean createSDKInstance() {
        if (ConstantsFace.HK_NET_SDK == null) {
            log.info("---system---" + SystemUtil.getOsInfo().isMac());
            synchronized (HCNetSDK.class) {
                String strDllPath = "";
                try {
                    String libPath = properties.getHk().getLibPath();
                    if (SystemUtil.getOsInfo().isWindows()) {
                        //win系统加载库路径
                        strDllPath = libPath + "\\HCNetSDK.dll";
                    }
                    else if (SystemUtil.getOsInfo().isLinux()) {
                        //Linux系统加载库路径
                        strDllPath = libPath + "/libhcnetsdk.so";
                    }
                    ConstantsFace.HK_NET_SDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class);
                } catch (Exception ex) {
                    log.error("loadLibrary: " + strDllPath + " Error: " + ex.getMessage());
                    return false;
                }
            }
        }
        return true;
    }


    public boolean getThermalCap(HkCameraDriver cameraDriver) {
        //通常最后一个通道是测温通道
        String thermalCap = this.getCap(cameraDriver.getIp(), cameraDriver.getUserId(), cameraDriver.getChannelNum(), 3634);
        if (StrUtil.isBlank(thermalCap)) {
            log.error("[HK]{}获取测温能力集失败", cameraDriver.getIp());
            return false;
        }
        cameraDriver.setSupportThermometry(XML.toJSONObject(thermalCap).getJSONObject("ThermalCap").getBool("isSupportThermometry", false));
        if (cameraDriver.isSupportThermometry()) {
            log.info("[HK]{},支持测温", cameraDriver.getIp());
            return true;
        }
        log.info("[HK]{},不支持/未开启测温", cameraDriver.getIp());
        return false;
    }


    private String changeRegionXml(List<TemplateArea> areaTemplateMeasurements, boolean clearFlag) {
        String regionXml = "";
        Document regionDocument = null;
        try {
            ClassPathResource resource = new ClassPathResource("region.xml");
            if (resource.exists()) {
                //默认的
                InputStream inputStream = resource.getInputStream();
                regionDocument = saxReader.read(resource.getInputStream());
                inputStream.close();
            }
            Element element = regionDocument.getRootElement().element("ThermometryRegionList");
            List<Element> xmlRegionList = element.elements();
            Element nowRule = xmlRegionList.get(0);
            //xmlRegionList.remove(0);
            xmlRegionList.clear();
            //这边的21是海康web界面看的可以设置的个数
            for (int i = 0; i < 21; i++) {
                Element newRule = nowRule.createCopy();
                if (i < Optional.ofNullable(areaTemplateMeasurements).map(List::size).orElse(0)) {
                    TemplateArea area = areaTemplateMeasurements.get(i);
                    String[] bottomLeft = StringUtils.split(area.getBottomLeftPos(), ",");
                    String[] bottomRight = StringUtils.split(area.getBottomRightPos(), ",");
                    String[] upperRight = StringUtils.split(area.getUpperRightPos(), ",");
                    String[] upperLeft = StringUtils.split(area.getUpperLeftPos(), ",");
                    List<String> posX = Arrays.asList(bottomLeft[0], bottomRight[0], upperRight[0], upperLeft[0]);
                    List<String> posY = Arrays.asList(bottomLeft[1], bottomRight[1], upperRight[1], upperLeft[1]);
                    List<Element> regionPointList = newRule.element("Region").element("RegionCoordinatesList").elements();
                    regionPointList.get(0).element("positionX").setText(posX.get(0));
                    regionPointList.get(0).element("positionY").setText(posY.get(0));
                    regionPointList.get(1).element("positionX").setText(posX.get(1));
                    regionPointList.get(1).element("positionY").setText(posY.get(1));
                    regionPointList.get(2).element("positionX").setText(posX.get(2));
                    regionPointList.get(2).element("positionY").setText(posY.get(2));
                    regionPointList.get(3).element("positionX").setText(posX.get(3));
                    regionPointList.get(3).element("positionY").setText(posY.get(3));
                    newRule.element("id").setText(String.valueOf(i + 1));
                    //name在画面上不显示，设置检测项名称没有意义。所以直接设置为CheckItemId，用来对应检测项；
                    newRule.element("name").setText(String.valueOf(area.getCheckItemId()));
                    newRule.element("enabled").setText("true");
                } else {
                    newRule.element("id").setText(String.valueOf(i + 1));
                    //覆盖旧的
                    newRule.element("enabled").setText("false");
                    if (i == 0 && !clearFlag) {
                        newRule.element("enabled").setText("true");
                    }
                }
                xmlRegionList.add(i, newRule);
            }
            regionXml = regionDocument.asXML();
        } catch (Exception e) {
            log.error("[HK] change region XML error:{}", e.getMessage());
        }
        return regionXml;
    }

    public String getImageCap(HkCameraDriver cameraDriver) {
        //一般取第一个通道的
        String ip = cameraDriver.getIp();
        int channel = cameraDriver.getStartChannel();
        int userId = cameraDriver.getUserId();
        String requestUrL = HCNetSDK.GET_IMAGE_CAP_URL.replace("channelValue", String.valueOf(channel));//"GET /ISAPI/PTZCtrl/channels/"+channel+"/status";
        String imageCap = this.sendXmlConfig(ip, userId, requestUrL, "");
        log.info("[HK]{},get image cap:{}", ip, imageCap);
        return imageCap;
    }


    /**
     * 手动模式下获取聚焦值
     * @param ip
     * @param userId
     * @param channel
     * @return
     */
    public String getZoomFocus(String ip, Integer userId, Integer channel) {
        String requestUrL = HCNetSDK.GET_FOCUS_ZOOM_URL.replace("channelValue",
                String.valueOf(channel));
        String ptzInfo = this.sendXmlConfig(ip, userId, requestUrL, "");
        //log.debug("[HK]{},get ptz xml info:{}", ip, ptzInfo);
        log.info("[HK]get ptz zoom info:{}", ptzInfo);
        return ptzInfo;
    }

    /**
     * 手动模式下设置聚焦值
     * @param ip
     * @param userId
     * @param channel
     * @return
     */
    public boolean setZoomFocus(String ip, Integer userId, Integer channel,int pqrsZoom, int mnstFocus) {
        String requestUrL = HCNetSDK.SET_FOCUS_ZOOM_URL.replace("channelValue", String.valueOf(channel));
        try {
            Document document = DocumentHelper.parseText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                    "<ZoomFocus version=\"2.0\" xmlns=\"http://www.hikvision.com/ver20/XMLSchema\">" +
                    "<pqrsZoom>15022</pqrsZoom>" +
                    "<mnstFocus>35591</mnstFocus>" +
                    "</ZoomFocus>");
            document.getRootElement().element("pqrsZoom").setText(String.valueOf(pqrsZoom));
            document.getRootElement().element("mnstFocus").setText(String.valueOf(mnstFocus));
            log.info("{} 设置相机数据: {}", ip,  document.asXML());
            sendXmlConfig(ip, userId, requestUrL, document.asXML());
        } catch (Exception e) {
            log.error("[HK]{},set PtzInfo Xml failed,{}", ip, e);
            return false;
        }
        return true;
    }


    public String sendXmlConfig(String ip, Integer userId, String requestUrL, String requestParamXml) {

        HCNetSDK.NET_DVR_XML_CONFIG_INPUT struXMLInput = new HCNetSDK.NET_DVR_XML_CONFIG_INPUT();
        struXMLInput.read();

        struXMLInput.dwSize = struXMLInput.size();

        HCNetSDK.BYTE_ARRAY stringRequest = new HCNetSDK.BYTE_ARRAY(1024);
        stringRequest.read();

        System.arraycopy(requestUrL.getBytes(), 0, stringRequest.byValue, 0, requestUrL.length());
        stringRequest.write();
        struXMLInput.lpRequestUrl = stringRequest.getPointer();
        struXMLInput.dwRequestUrlLen = requestUrL.length();

        //输入XML文本，GET命令不传输入文本
        try {
            int inputDataLen =requestParamXml.getBytes(StandardCharsets.UTF_8).length;

            if(inputDataLen > 0)
            {
                HCNetSDK.BYTE_ARRAY stringInBuffer = new HCNetSDK.BYTE_ARRAY(inputDataLen);
                stringInBuffer.read();
                System.arraycopy(requestParamXml.getBytes(StandardCharsets.UTF_8), 0, stringInBuffer.byValue, 0, inputDataLen);
                stringInBuffer.write();
                struXMLInput.lpInBuffer = stringInBuffer.getPointer();
                struXMLInput.dwInBufferSize = inputDataLen;
            } else {
                struXMLInput.lpInBuffer = null;
                struXMLInput.dwInBufferSize = 0;
            }

            struXMLInput.write();

            HCNetSDK.BYTE_ARRAY stringXMLOut = new HCNetSDK.BYTE_ARRAY(8 * 1024);
            stringXMLOut.read();
            HCNetSDK.BYTE_ARRAY struXMLStatus = new HCNetSDK.BYTE_ARRAY(1024);
            struXMLStatus.read();

            HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT struXMLOutput = new HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT();
            struXMLOutput.read();
            struXMLOutput.dwSize = struXMLOutput.size();
            struXMLOutput.lpOutBuffer = stringXMLOut.getPointer();
            struXMLOutput.dwOutBufferSize = stringXMLOut.size();
            struXMLOutput.lpStatusBuffer = struXMLStatus.getPointer();
            struXMLOutput.dwStatusSize = struXMLStatus.size();
            struXMLOutput.write();
            log.info("透传发送的xml: {}, {}", requestUrL, requestParamXml);
            boolean ret = hcNetSDK.NET_DVR_STDXMLConfig(userId, struXMLInput, struXMLOutput);
            if (!ret) {
                log.error("[HK]{},send xml failed:{}", ip, hcNetSDK.NET_DVR_GetLastError());
                return null;
            }
            stringXMLOut.read();
            //打印输出XML文本
            String strOutXML = new String(stringXMLOut.byValue).trim();
            log.info("发送XMl结构 {} 以及返回结果是 {}", requestParamXml ,strOutXML);
            struXMLStatus.read();
            return strOutXML;
        }catch (Exception e) {
            log.info("[HK]{},send xml failed:{}", e.getMessage());
        }
        return null;
    }

    public boolean getPTZAbsoluteExCap(HkCameraDriver cameraDriver) {
        String ptzAbsoluteExCap = this.getCap(cameraDriver.getIp(), cameraDriver.getUserId(), cameraDriver.getStartChannel(), 6695);
        if (StrUtil.isBlank(ptzAbsoluteExCap)) {
            log.error("[HK]{}获取高精度PTZ绝对位置配置能力集失败", cameraDriver.getIp());
            return false;
        }
        return true;
    }
    public boolean getPTZCap(HkCameraDriver cameraDriver) {
        String ptzCap = this.getCap(cameraDriver.getIp(), cameraDriver.getUserId(), cameraDriver.getStartChannel(), 3619);
        if (StrUtil.isBlank(ptzCap)) {
            log.error("[HK]{}获取Ptz能力集失败", cameraDriver.getIp());
            return false;
        }
        JSONObject jsonObject = XML.toJSONObject(ptzCap);
        //是否支持3D定位
        Boolean isSupportPosition3D = jsonObject.getJSONObject("PTZChanelCap").getBool("isSupportPosition3D", false);
        cameraDriver.setSupportPosition3D(isSupportPosition3D);
        return true;
    }
    public void setRegion(HkCameraDriver cameraDriver, List<TemplateArea> areaTemplateMeasurements, boolean clearFlag) {
        try {
            HCNetSDK.NET_DVR_STD_CONFIG lpnet_dvr_std_config = new HCNetSDK.NET_DVR_STD_CONFIG();
            String regionXml = this.changeRegionXml(areaTemplateMeasurements, clearFlag);

            HCNetSDK.NET_DVR_THERMOMETRY_COND net_dvr_thermometry_cond = new HCNetSDK.NET_DVR_THERMOMETRY_COND();
            net_dvr_thermometry_cond.dwSize = net_dvr_thermometry_cond.size();
            net_dvr_thermometry_cond.dwChannel = cameraDriver.getChannelNum();
            net_dvr_thermometry_cond.wPresetNo = 1;
            net_dvr_thermometry_cond.write();

            lpnet_dvr_std_config.lpCondBuffer = net_dvr_thermometry_cond.getPointer();
            lpnet_dvr_std_config.dwCondSize = net_dvr_thermometry_cond.size();
            lpnet_dvr_std_config.lpInBuffer = Pointer.NULL;
            lpnet_dvr_std_config.dwInSize = 0;
            lpnet_dvr_std_config.dwOutSize = 0;
            lpnet_dvr_std_config.dwStatusSize = 0;
            lpnet_dvr_std_config.byDataType = 1;
            lpnet_dvr_std_config.lpXmlBuffer = regionXml;
            lpnet_dvr_std_config.dwXmlSize = regionXml.length();
            lpnet_dvr_std_config.write();
            if (!hcNetSDK.NET_DVR_SetSTDConfig(cameraDriver.getUserId(), 3625, lpnet_dvr_std_config)) {
                log.error("[HK]{} init Region failed：{}", cameraDriver.getIp(), hcNetSDK.NET_DVR_GetLastError());
                return;
            }
            log.info("[HK]{} init Region success", cameraDriver.getIp());
        } catch (Exception e) {
            log.error("[HK]{} init Region error：{}", cameraDriver.getIp(), e.getMessage());
        }
    }
    private String getCap(String ip, Integer userId, Integer channel, Integer dwAbilityType) {
        HCNetSDK.NET_DVR_STD_ABILITY net_dvr_std_ability = new HCNetSDK.NET_DVR_STD_ABILITY();
        net_dvr_std_ability.read();
        //通常最后一个通道是测温通道
        IntByReference lchannel = new IntByReference(channel);
        Memory repBuff = new Memory(1024 * 64);
        net_dvr_std_ability.lpCondBuffer = lchannel.getPointer();
        net_dvr_std_ability.dwCondSize = 4;
        net_dvr_std_ability.lpOutBuffer = repBuff;
        net_dvr_std_ability.dwOutSize = (int) repBuff.getSize();
        net_dvr_std_ability.lpStatusBuffer = null;
        net_dvr_std_ability.dwStatusSize = 4;
        net_dvr_std_ability.write();
        if (!hcNetSDK.NET_DVR_GetSTDAbility(userId, dwAbilityType, net_dvr_std_ability)) {
            log.error("[HK]{},获取{}能力集失败,{}", ip, dwAbilityType, hcNetSDK.NET_DVR_GetLastError());
            return null;
        }
        net_dvr_std_ability.read();
        String cap = net_dvr_std_ability.lpOutBuffer.getString(0);
        //isSupportPosition3D 是否支持3D定位
        log.info("[HK]{},{}能力集：{}", ip, dwAbilityType, cap);
        return cap;
    }
    /**
     * 获取测温模式能力集
     */
    public boolean getThermometryMode(HkCameraDriver cameraDriver) {
        //通常最后一个通道是测温通道
        String thermometryMode = this.getCap(cameraDriver.getIp(), cameraDriver.getUserId(), cameraDriver.getChannelNum(), 6764);
        if (StrUtil.isBlank(thermometryMode)) {
            log.error("[HK]{}获取测温模式能力集失败", cameraDriver.getIp());
            return false;
        }
        String thermometryOpt = XML.toJSONObject(thermometryMode).getJSONObject("ThermometryMode").getJSONObject("mode").getStr("opt", "normal");
        if (thermometryOpt.contains("expert")) {
            //有专家模式就用专家模式
            cameraDriver.setThermometryMode(1);
        }
        return true;
    }

    public void initThermometryMode(HkCameraDriver cameraDriver) {
        try {
            HCNetSDK.NET_DVR_STD_CONFIG lpnet_dvr_std_config = new HCNetSDK.NET_DVR_STD_CONFIG();
            Pointer pointer = new Memory(4);
            pointer.setInt(0, cameraDriver.getChannelNum());

            HCNetSDK.NET_DVR_THERMOMETRY_MODE net_dvr_thermometry_mode = new HCNetSDK.NET_DVR_THERMOMETRY_MODE();
            net_dvr_thermometry_mode.byMode = (byte) cameraDriver.getThermometryMode();
            net_dvr_thermometry_mode.byThermometryROIEnabled = 0;
            net_dvr_thermometry_mode.dwSize = net_dvr_thermometry_mode.size();
            net_dvr_thermometry_mode.write();

            lpnet_dvr_std_config.lpCondBuffer = pointer;
            lpnet_dvr_std_config.dwCondSize = 4;
            lpnet_dvr_std_config.lpInBuffer = net_dvr_thermometry_mode.getPointer();
            lpnet_dvr_std_config.dwInSize = net_dvr_thermometry_mode.size();
            lpnet_dvr_std_config.dwOutSize = 0;
            lpnet_dvr_std_config.dwStatusSize = 0;
            lpnet_dvr_std_config.byDataType = 0;
            lpnet_dvr_std_config.lpXmlBuffer = "";
            lpnet_dvr_std_config.dwXmlSize = 0;
            lpnet_dvr_std_config.write();
            if (!hcNetSDK.NET_DVR_SetSTDConfig(cameraDriver.getUserId(), HCNetSDK.NET_DVR_SET_THERMOMETRY_MODE, lpnet_dvr_std_config)) {
                log.error("[HK]{} init thermometry mode failed:{}", cameraDriver.getIp(), hcNetSDK.NET_DVR_GetLastError());
                return;
            }
            log.info("[HK]{} init thermometry mode success", cameraDriver.getIp());
        } catch (Exception e) {
            log.error("[HK]{} init thermometry mode error:{}", cameraDriver.getIp(), e.getMessage());
        }
    }


    @Override
    public IEquipmentCmdResult executeCmd(QueryParamsDto dto, boolean async, EquipmentCmdEnum cmdEnum, IEquipmentCmdParamsDto cmdParamsDto) {
        HkCameraDriver cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(dto.getIp());
        if (cmdEnum == EquipmentCmdEnum.TAKE_PIC) {
            TakePicParamsDto picParamsDto = (TakePicParamsDto) cmdParamsDto;
            return this.takePic(dto.getIp(), dto.getChannel(), picParamsDto.getBasePath(), picParamsDto.getFolder());
        } else if (cmdEnum == EquipmentCmdEnum.PTZ_MOVE) {
            PtzCtrlDTO ptzCtrlDTO = (PtzCtrlDTO) cmdParamsDto;
            this.ptzCtrl(ptzCtrlDTO);
        } else if (cmdEnum == EquipmentCmdEnum.FOCUS_MODE) {
            CameraParamDTO cameraParamDTO = (CameraParamDTO) cmdParamsDto;
            //byFocusMode
            List<EquipmentParamsDto> paramsDtos = Lists.newArrayList();
            paramsDtos.add(new EquipmentParamsDto("byFocusMode", "byFocusMode", String.valueOf(cameraParamDTO.getByFocusMode()), ""));
            this.setFocusModeParam(dto.getIp(), cameraDriver.getUserId(),
                    1, paramsDtos);
        } else if (cmdEnum == EquipmentCmdEnum.START_RECORD) {
            TakePicParamsDto picParamsDto = (TakePicParamsDto) cmdParamsDto;
            this.startRecord(dto.getIp(), dto.getChannel(), picParamsDto.getBasePath(), picParamsDto.getFolder());
        } else if (cmdEnum == EquipmentCmdEnum.STOP_RECORD) {
            TakePicParamsDto picParamsDto = (TakePicParamsDto) cmdParamsDto;
            return this.stopRecord(dto.getIp(), dto.getChannel(), picParamsDto.getBasePath(), picParamsDto.getFolder());
        } else if (cmdEnum == EquipmentCmdEnum.INIT_REGION_AND_START_MONITOR) {
            InitRegionMeasureDTO initRegionMeasureDTO = (InitRegionMeasureDTO) cmdParamsDto;
            List<Integer> configId = this.initRegionAndStartMonitor(initRegionMeasureDTO);
            MeasureTemplateDto rules = new MeasureTemplateDto();
            rules.setConfig(configId);
            return rules;
        } else if (cmdEnum == EquipmentCmdEnum.STOP_MONITOR) {
            InitRegionMeasureDTO initRegionMeasureDTO = (InitRegionMeasureDTO) cmdParamsDto;
            return this.stopRegionMonitor(initRegionMeasureDTO);
        }

        return null;
    }


    private boolean NET_DVR_PTZPreset_Other(String ip, Integer lchannel, Integer cmd, Integer pressIndex) {
        HkCameraDriver hkCameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(ip);
        if (!hcNetSDK.NET_DVR_PTZPreset_Other(hkCameraDriver.getUserId(), lchannel, cmd, pressIndex)) {
            log.error("[HK]{} ptz_preset cmd：{},failed：{}", ip, cmd, hcNetSDK.NET_DVR_GetLastError());
            return false;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }

    public List<Integer> initRegionAndStartMonitor(InitRegionMeasureDTO dto) {
        try {
            HkCameraDriver cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(dto.getIp());
            if (cameraDriver.getThermometryMode() == 1) {
                //专家模式需要设置规则
                //设置预置点
                this.NET_DVR_PTZPreset_Other(cameraDriver.getIp(), cameraDriver.getChannelNum(), 8, 1);
                this.NET_DVR_PTZPreset_Other(cameraDriver.getIp(), cameraDriver.getChannelNum(), 39, 1);
                //绑定规则
                this.setRegion(cameraDriver, dto.getTemplateAreas(), false);
            }
            //开启测温
            HCNetSDK.NET_DVR_REALTIME_THERMOMETRY_COND struThermCond = new HCNetSDK.NET_DVR_REALTIME_THERMOMETRY_COND();
            struThermCond.byRuleID = (byte) 0;//获取全部规则
            struThermCond.byMode = 1;
            struThermCond.dwChan = cameraDriver.getChannelNum();
            struThermCond.dwSize = struThermCond.size();
            struThermCond.write();
            synchronized (HkTemperatureCallBack.class) {
                if (ConstantsFace.hkTemperatureCallBack == null) {
                    ConstantsFace.hkTemperatureCallBack = new HkTemperatureCallBack();
                }
            }
            Pointer pUserData = cameraDriver.getPUserData();
            int stopThermometryHandle = hcNetSDK.NET_DVR_StartRemoteConfig(cameraDriver.getUserId(), 3629,
                    struThermCond.getPointer(), struThermCond.size(), ConstantsFace.hkTemperatureCallBack, pUserData);
            if (stopThermometryHandle == -1) {
                log.error("[HK]{} startMonitor failed:{}", dto.getIp(), hcNetSDK.NET_DVR_GetLastError());
                return null;
            }
            log.info("[HK]{} startMonitor success", dto.getIp());
            cameraDriver.setStopThermometryHandle(stopThermometryHandle);
            Integer size = Math.max(1,Optional.ofNullable(dto).map(InitRegionMeasureDTO::getTemplateAreas).map(List::size).orElse(1));
            return IntStream.rangeClosed(1, size).boxed().collect(Collectors.toList());
        } catch (Exception e) {
            log.error("[HK]{} login error:{}", dto.getIp(), hcNetSDK.NET_DVR_GetLastError());
            return null;
        }
    }


    public AreaTemplateDTO stopRegionMonitor(InitRegionMeasureDTO dto) {
        HkCameraDriver cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(dto.getIp());
        Integer stopThermometryHandle = cameraDriver.getStopThermometryHandle();
        if (stopThermometryHandle != null) {
            if (hcNetSDK.NET_DVR_StopRemoteConfig(stopThermometryHandle)) {
                log.info("[HK]{} stop Monitor success", dto.getIp());
                cameraDriver.setStopThermometryHandle(null);
                if (cameraDriver.getThermometryMode() == 1) {
                    this.setRegion(cameraDriver, null, true);
                }
            } else {
                log.error("[HK]{} stop Monitor failed:{}", dto.getIp(), hcNetSDK.NET_DVR_GetLastError());
                throw new EquipmentException(String.format("[HK]%s stop Monitor failed:%s", dto.getIp(), hcNetSDK.NET_DVR_GetLastError()));
            }
        }
        if (ConstantsFace.TEMPERATURE_TABLE.isEmpty()){
            return new AreaTemplateDTO();
        }
        return ConstantsFace.TEMPERATURE_TABLE.get(dto.getIp(),dto.getRuleIdList().get(0));
    }

    public boolean ptzCtrl(PtzCtrlDTO dto) {
        HkCameraDriver cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(dto.getIp());
        Integer ptzChannel = Optional.ofNullable(dto.getChannel()).orElse(cameraDriver.getStartChannel());
        Integer flag = Optional.ofNullable(dto.getFlag()).orElse(1);
        if (!hcNetSDK.NET_DVR_PTZControl_Other(cameraDriver.getUserId(), ptzChannel, dto.getCtrlType(), flag)) {
            log.error("[HK]{},channel:{},ptz cmd:{},flag:{},failed:{}", cameraDriver.getIp(), ptzChannel, dto.getCtrlType(), flag, hcNetSDK.NET_DVR_GetLastError());
            throw new EquipmentException(String.format("控制失败. [HK]%s,channel:%s,ptz cmd:%s,flag:%s,failed:%s", cameraDriver.getIp(), ptzChannel, dto.getCtrlType(), flag, hcNetSDK.NET_DVR_GetLastError()));
        }
        log.info("[HK]{},channel:{},ptz cmd:{},flag:{},success", cameraDriver.getIp(), ptzChannel, dto.getCtrlType(), flag);
        return Boolean.TRUE;
    }


    public MediaFileDto startRecord(String ip, Integer channel,String basePath,String folder) {
        HkCameraDriver cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(ip);
        int recordChannel = Math.min(Optional.ofNullable(channel).orElse(cameraDriver.getStartChannel()), cameraDriver.getChannelNum());
        //开启预览
        Integer realPlayId = cameraDriver.getRealPlayId();
        if (realPlayId != null) {
            throw new EquipmentException("已经开启录制，请勿重复调用");
        }
        HCNetSDK.NET_DVR_PREVIEWINFO m_strClientInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();
        m_strClientInfo.lChannel = recordChannel;
        m_strClientInfo.dwStreamType = 0;   //码流类型：0-主码流，1-子码流，2-三码流，3-虚拟码流，以此类推
        m_strClientInfo.dwLinkMode = 0;
        m_strClientInfo.bBlocked = 0;
        m_strClientInfo.byPreviewMode = 0;
        m_strClientInfo.bPassbackRecord = 0;
        m_strClientInfo.byProtoType = 1;
        m_strClientInfo.byVideoCodingType = 0;
        m_strClientInfo.byNPQMode = 0;
        m_strClientInfo.write();
        realPlayId = hcNetSDK.NET_DVR_RealPlay_V40(cameraDriver.getUserId(), m_strClientInfo, null, null);
        if (realPlayId == -1) {
            log.error("[HK]{},channel:{},real play failed:{}", ip, recordChannel, hcNetSDK.NET_DVR_GetLastError());
            throw new EquipmentException(String.format("开启预览失败.[HK]%s,channel:%s,real play failed:%s", ip, recordChannel, hcNetSDK.NET_DVR_GetLastError()));
        }
        cameraDriver.setRealPlayId(realPlayId);
        log.info("[HK]{},channel:{},real play success realPlayId：{}", ip, recordChannel, realPlayId);
        //保存到本地
        String fileFolder = basePath + File.separator + folder + File.separator;
        String videoName = DateUtil.format(new Date(), "yyyyMMddHHmmssSSS") + "_" + realPlayId + ".mp4";
        cameraDriver.setVideoName(videoName);
        if (!FileUtil.exist(fileFolder)) {
            FileUtil.mkdir(fileFolder);
        }
        File file = new File(fileFolder + videoName);
        if (!hcNetSDK.NET_DVR_SaveRealData(realPlayId, file.getAbsolutePath())) {
            log.error("[HK]{},channel:{}，path:{},save real data failed:{}", ip, recordChannel, file.getAbsolutePath(), hcNetSDK.NET_DVR_GetLastError());
            throw new EquipmentException(String.format("保存视频失败.[HK]%s,channel:%s,save real data failed:%s", ip, recordChannel, file.getAbsolutePath(), hcNetSDK.NET_DVR_GetLastError()));
        }
        log.info("[HK]{},channel:{},save real data success：{}", ip, recordChannel, file.getAbsolutePath());
        MediaFileDto dto = new MediaFileDto();
        dto.setName(videoName);
        dto.setRelativePath(File.separator + folder + File.separator + videoName);
        dto.setAbsolutePath(file.getAbsolutePath());
        return dto;
    }



    public MediaFileDto stopRecord(String ip, Integer channel,String basePath,String folder) {
        HkCameraDriver cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(ip);
        int recordChannel = Math.min(Optional.ofNullable(channel).orElse(cameraDriver.getStartChannel()), cameraDriver.getChannelNum());
        Integer realPlayId = cameraDriver.getRealPlayId();
        if (realPlayId == null) {
            throw new EquipmentException("未开启预览");
        }
        if (!hcNetSDK.NET_DVR_StopSaveRealData(realPlayId)) {
            log.error("[HK]{},channel:{},stop save real data failed:{}", ip, recordChannel, hcNetSDK.NET_DVR_GetLastError());
        }
        if (!hcNetSDK.NET_DVR_StopRealPlay(realPlayId)) {
            log.error("[HK]{},channel:{},stop real play failed:{}", ip, recordChannel, hcNetSDK.NET_DVR_GetLastError());
            throw new EquipmentException(String.format("停止预览视频失败.[HK]%s,channel:%s,stop real play failed:%s", ip, recordChannel, hcNetSDK.NET_DVR_GetLastError()));
        }
        log.info("[HK]{},channel:{},stop real play success", ip, recordChannel);
        cameraDriver.setRealPlayId(null);
        String fileFolder = basePath + File.separator + folder + File.separator;
        String videoName = cameraDriver.getVideoName();
        if (StrUtil.isBlank(videoName)){
            log.error("[HK]{},channel:{},get videoName failed", ip, recordChannel);
            throw new EquipmentException(String.format("请先开始录制.[HK]%s,channel:%s,get videoName failed", ip, recordChannel));
        }
        File file = new File(fileFolder + videoName);
        if (!file.exists()) {
            log.error("[HK]{},channel:{},file not exist：{}", ip, recordChannel, file.getAbsolutePath());
            throw new EquipmentException(String.format("文件不存在. [HK]%s,channel:%s,file not exist：%s", ip, recordChannel, file.getAbsolutePath()));
        }
        cameraDriver.setVideoName(null);
        MediaFileDto dto = new MediaFileDto();
        dto.setName(file.getName());
        dto.setRelativePath(File.separator + folder + File.separator + videoName);
        dto.setAbsolutePath(file.getAbsolutePath());
        return dto;
    }
    public MediaFileDto takePic(String ip, Integer channel, String basePath, String folder) {
        HkCameraDriver cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(ip);
        int takePicChannel = Math.min(Optional.ofNullable(channel).orElse(cameraDriver.getStartChannel()), cameraDriver.getChannelNum());
        HCNetSDK.NET_DVR_JPEGPARA jpegpara = new HCNetSDK.NET_DVR_JPEGPARA();
        jpegpara.wPicSize = 3;
        jpegpara.wPicQuality = 0;
        Memory jpegBuffer = new Memory(8 * 1024 * 1024);
        IntByReference a = new IntByReference();
        if (!hcNetSDK.NET_DVR_CaptureJPEGPicture_NEW(cameraDriver.getUserId(), takePicChannel, jpegpara, jpegBuffer, 8 * 1024 * 1024, a)) {
            log.error("[HK]{},channel:{},take pic failed:{}", ip, takePicChannel, hcNetSDK.NET_DVR_GetLastError());
            throw new EquipmentException(String.format("拍照失败. [HK]%s,channel:%s,take pic failed:%s", ip, takePicChannel, hcNetSDK.NET_DVR_GetLastError()));
        }
        log.info("[HK]{},channel:{},take pic success,byteLength:{},", ip, takePicChannel, a.getValue());
        //保存到本地
        String fileFolder = basePath + File.separator + folder + File.separator;
        String pictureName = DateUtil.format(new Date(), "yyyyMMddHHmmssSSS")+"_"+ RandomUtil.randomInt(100) + ".jpeg";
        if (!FileUtil.exist(fileFolder)) {
            FileUtil.mkdir(fileFolder);
        }
        File file = FileUtil.writeBytes(jpegBuffer.getByteArray(0, a.getValue()), fileFolder + pictureName);
        if (file.exists()) {
            log.info("[HK]{},channel:{},save pic local success:{}", ip, takePicChannel, file.getAbsolutePath());
        } else {
            log.error("[HK]{},channel:{},save pic local failed:{}", ip, takePicChannel, folder + pictureName);
            throw new EquipmentException(String.format("照片保存到本地失败. [HK]%s,channel:%s,save pic local failed:%s", ip, takePicChannel, folder + pictureName));
        }
        MediaFileDto dto = new MediaFileDto();
        dto.setName(pictureName);
        dto.setRelativePath(File.separator + folder + File.separator + pictureName);
        dto.setAbsolutePath(file.getAbsolutePath());
        return dto;
    }


    @Override
    public List<EquipmentParamsDto> getDeviceParamsData(QueryParamsDto dto, EquipmentCmdEnum cmdEnum) {
        String pose = paramsCacheTable.get(dto.getIp(), cmdEnum.getDesc());
        if (StrUtil.isNotBlank(pose)){
            try {
                List<EquipmentParamsDto> equipmentParamsDtos = JSONArray.parseArray(pose, EquipmentParamsDto.class);
                if (!equipmentParamsDtos.isEmpty()) {
                    return equipmentParamsDtos;
                }
            }catch (Exception e){
                log.error("[hk] parse pose array error {}",e.getMessage());
            }
        }
        List<EquipmentParamsDto> paramsDtos = Lists.newArrayList();
        if (cmdEnum == EquipmentCmdEnum.GET_ORIGIN_PARAMS) {
            paramsDtos.add(CameraParamDTO.createParamsDto("absoluteZoom",  "absoluteZoom", "10", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("azimuth",  "azimuth", "0", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("elevation",  "elevation", "0", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("byFocusMode",  "byFocusMode", "2", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("fPan",  "fPan", "0.0", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("fTilt",  "fTilt", "0.0", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("zoom",  "zoom", "1.0", ""));
        } else if (cmdEnum == EquipmentCmdEnum.GET_BACK_PARAMS_DATA) {
            paramsDtos.add(CameraParamDTO.createParamsDto("absoluteZoom",  "absoluteZoom", "10", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("azimuth",  "azimuth", "1800", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("elevation",  "elevation", "0", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("byFocusMode",  "byFocusMode", "2", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("fPan",  "fPan", "180.0", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("fTilt",  "fTilt", "0.0", ""));
            paramsDtos.add(CameraParamDTO.createParamsDto("zoom",  "zoom", "1.0", ""));
        }

        return paramsDtos;
    }





    @Override
    public boolean setEquipmentParams(QueryParamsDto dto,  List<EquipmentParamsDto> paramsDtos, boolean async) {
        try {
            HkCameraDriver cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(dto.getIp());
            if (cameraDriver == null) {
                EquipmentLoginParamsDto equipmentLoginParamsDto = new EquipmentLoginParamsDto();
                equipmentLoginParamsDto.setIp(dto.getIp());
                equipmentLoginParamsDto.setPassword(dto.getPassword());
                equipmentLoginParamsDto.setPort(dto.getPort());
                equipmentLoginParamsDto.setUsername(dto.getUsername());
                this.login(equipmentLoginParamsDto);
                cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(dto.getIp());
            }
            Integer ptzChannel = Optional.ofNullable(dto.getChannel()).orElse(cameraDriver.getStartChannel());
            //设置对焦模式
            this.setFocusModeParam(dto.getIp(), cameraDriver.getUserId(), ptzChannel, paramsDtos);

            Optional<EquipmentParamsDto> absoluteZoomDto = paramsDtos.stream().filter(t -> "absoluteZoom".equals(t.getKey())).findFirst();
            Optional<EquipmentParamsDto> azimuthDto = paramsDtos.stream().filter(t -> "azimuth".equals(t.getKey())).findFirst();
            Optional<EquipmentParamsDto> elevationDto = paramsDtos.stream().filter(t -> "elevation".equals(t.getKey())).findFirst();
            if (absoluteZoomDto.isPresent() && azimuthDto.isPresent() && elevationDto.isPresent()) {
                int elevation = Integer.parseInt(elevationDto.get().getValue());
                int azimuth = Integer.parseInt(azimuthDto.get().getValue());
                int absoluteZoom = Integer.parseInt(absoluteZoomDto.get().getValue());
                this.setPtzInfoXml(dto.getIp(), cameraDriver.getUserId(), ptzChannel,
                        elevation,
                        azimuth,
                        absoluteZoom);
                if (async) {
                    //异步立刻返回
                    return true;
                }
                //同步-检测是否到位
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(1000);
                    List<EquipmentParamsDto> params = this.getEquipmentParams(dto);
                    Optional<EquipmentParamsDto> currentZoomDto = params.stream().filter(t -> "absoluteZoom".equals(t.getKey())).findFirst();
                    Optional<EquipmentParamsDto> currentAzimuthDto = params.stream().filter(t -> "azimuth".equals(t.getKey())).findFirst();
                    Optional<EquipmentParamsDto> currentElevationDto = params.stream().filter(t -> "elevation".equals(t.getKey())).findFirst();
                    if (currentZoomDto.isEmpty() || currentAzimuthDto.isEmpty() || currentElevationDto.isEmpty()) {
                        continue;
                    }
                    float currentZoom = Float.parseFloat(currentZoomDto.get().getValue());
                    float currentAzimuth = Float.parseFloat(currentAzimuthDto.get().getValue());
                    float currentElevation = Float.parseFloat(currentElevationDto.get().getValue());
                    if (Math.abs(currentZoom - absoluteZoom) < 10 && Math.abs(currentAzimuth - azimuth) < 30 && Math.abs(currentElevation-elevation) < 30) {
                        log.info("[HK]到位");
                        break;
                    }
                    //外部中断标志
                    if (this.getStopFlag()) {
                        log.info("[HK]外部中断,退出到位判断循环");
                        break;
                    }
                }
            }
            return true;
        }catch (Exception e){
            log.info("[HK]set param error,{}",e.getMessage());
            return false;
        }
    }

    /**
     * 透传协议设置ptz角度和zoom
     */
    private boolean setPtzInfoXml(String ip, Integer userId, Integer channel, int elevation, int azimuth, int absoluteZoom) {
        //一般取第一个通道的
        String requestUrL = HCNetSDK.SET_FOCUS_URL.replace("channelValue", String.valueOf(channel));//"GET /ISAPI/PTZCtrl/channels/"+channel+"/absolute";
        try {
            Document document = DocumentHelper.parseText(ConstantsFace.HK_SET_FOCUS_XML);
            document.getRootElement().element("AbsoluteHigh").element("elevation").setText(String.valueOf(elevation));
            document.getRootElement().element("AbsoluteHigh").element("azimuth").setText(String.valueOf(azimuth));
            document.getRootElement().element("AbsoluteHigh").element("absoluteZoom").setText(String.valueOf(absoluteZoom));
            this.sendXmlConfig(ip, userId, requestUrL, document.asXML());
        } catch (Exception e) {
            log.error("[HK]{},set PtzInfo Xml failed,{}", ip, e);
            return false;
        }
        return true;
    }

        /**
     * 设置高精度PTZ绝对位置配置
     */
    private boolean setPTZAbsoluteExParam(String ip, Integer userId, Integer channel,  List<EquipmentParamsDto> paramsDtos) {
        HCNetSDK.NET_DVR_PTZABSOLUTEEX_CFG ptzAbsoluteExParam = this.getPTZAbsoluteExParam(ip, userId, channel);
        Optional<EquipmentParamsDto> dwFocus = paramsDtos.stream().filter(t-> "dwFocus".equals(t.getKey())).findFirst();
        Optional<EquipmentParamsDto> fZoom = paramsDtos.stream().filter(t-> "fZoom".equals(t.getKey())).findFirst();
        Optional<EquipmentParamsDto> fTilt = paramsDtos.stream().filter(t-> "fTilt".equals(t.getKey())).findFirst();
        Optional<EquipmentParamsDto> fPan = paramsDtos.stream().filter(t-> "fPan".equals(t.getKey())).findFirst();
        Optional<EquipmentParamsDto> dwFocalLen = paramsDtos.stream().filter(t-> "dwFocalLen".equals(t.getKey())).findFirst();

        if (ptzAbsoluteExParam != null && dwFocus.isPresent() && fZoom.isPresent() && fTilt.isPresent() && fPan.isPresent() && dwFocalLen.isPresent()) {
            ptzAbsoluteExParam.struPTZCtrl.dwFocus = Integer.parseInt(dwFocus.get().getValue());
            ptzAbsoluteExParam.struPTZCtrl.fZoom = Float.parseFloat(fZoom.get().getValue());
            ptzAbsoluteExParam.struPTZCtrl.fTilt = Float.parseFloat(fTilt.get().getValue());
            ptzAbsoluteExParam.struPTZCtrl.fPan = Float.parseFloat(fPan.get().getValue());
            ptzAbsoluteExParam.struPTZCtrl.write();
            ptzAbsoluteExParam.dwFocalLen = Integer.parseInt(dwFocalLen.get().getValue());
            ptzAbsoluteExParam.fHorizontalSpeed = 1000f;
            ptzAbsoluteExParam.fVerticalSpeed = 1000f;
            ptzAbsoluteExParam.write();
            HCNetSDK.NET_DVR_STD_CONFIG lpnet_dvr_std_config_get = new HCNetSDK.NET_DVR_STD_CONFIG();
            Pointer pointer = new Memory(4);
            //通道号
            pointer.setInt(0, channel);
            lpnet_dvr_std_config_get.lpCondBuffer = pointer;
            lpnet_dvr_std_config_get.dwCondSize = 4;
            lpnet_dvr_std_config_get.lpInBuffer = ptzAbsoluteExParam.getPointer();
            lpnet_dvr_std_config_get.dwInSize = ptzAbsoluteExParam.size();
            lpnet_dvr_std_config_get.byDataType = 0;
            lpnet_dvr_std_config_get.write();
            boolean ret = hcNetSDK.NET_DVR_SetSTDConfig(userId, HCNetSDK.NET_DVR_SET_PTZABSOLUTEEX, lpnet_dvr_std_config_get);
            if (!ret) {
                log.error("[HK]{},channel:{},set ptz absolute param failed:{}", ip, channel, hcNetSDK.NET_DVR_GetLastError());
                return false;
            }
        }
        log.info("[HK]{},channel:{},set ptz absolute param success", ip, channel);
        return true;
    }


    private boolean setFocusModeParam(String ip, Integer userId, Integer channel, List<EquipmentParamsDto> paramsDtos) {
        HCNetSDK.NET_DVR_FOCUSMODE_CFG focusModeParam = this.getFocusModeParam(ip, userId, channel);
        //默认半自动
        Optional<EquipmentParamsDto> focusModeDto = paramsDtos.stream().filter(t-> "byFocusMode".equals(t.getKey())).findFirst();
        focusModeParam.byFocusMode = (byte) (focusModeDto.isPresent() ? Integer.parseInt(focusModeDto.get().getValue()) : 2);
        focusModeParam.byFocusSpeedLevel = 3;
        focusModeParam.byZoomSpeedLevel = 3;
        focusModeParam.byFocusDefinitionDisplay = 1;
        if (focusModeParam.byFocusMode ==1 ) {
            Optional<EquipmentParamsDto> dwFocusPos = paramsDtos.stream().filter(t-> "dwFocusPos".equals(t.getKey())).findFirst();
            focusModeParam.dwFocusPos = dwFocusPos.map(equipmentParamsDto -> Integer.parseInt(equipmentParamsDto.getValue())).orElse(0);
            Optional<EquipmentParamsDto> mnstFocus = paramsDtos.stream().filter(t-> "mnstFocus".equals(t.getKey())).findFirst();
            Optional<EquipmentParamsDto> pqrsZoom = paramsDtos.stream().filter(t-> "pqrsZoom".equals(t.getKey())).findFirst();
            if (mnstFocus.isPresent() && pqrsZoom.isPresent()) {
                // 透传接口， 设置参数
                this.setZoomFocus(ip, userId, channel, Integer.parseInt(pqrsZoom.get().getValue()),
                        Integer.parseInt(mnstFocus.get().getValue()));
            }
            // 云台可能有效果，普通相机没用
            this.setPTZAbsoluteExParam(ip, userId, channel, paramsDtos);
        }
        if (focusModeParam.byFocusMode == 2) {
            focusModeParam.byFocusSensitivity = 2;
        }
        focusModeParam.write();
        boolean ret = hcNetSDK.NET_DVR_SetDVRConfig(userId, HCNetSDK.NET_DVR_SET_FOCUSMODECFG, channel, focusModeParam.getPointer(), focusModeParam.size());
        if (!ret) {
            log.error("[HK]{},channel:{},set focus mode failed:{}", ip, channel, hcNetSDK.NET_DVR_GetLastError());
            return false;
        }
        log.info("[HK]{},channel:{},set focus mode success", ip, channel);
        return true;
    }


    @Override
    public List<EquipmentParamsDto> getEquipmentParams(QueryParamsDto dto) {
        List<EquipmentParamsDto>  params = Lists.newArrayList();
        dto.setPort(8000);
        HkCameraDriver cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(dto.getIp());
        if (cameraDriver == null) {
            EquipmentLoginParamsDto equipmentLoginParamsDto  = new EquipmentLoginParamsDto();
            equipmentLoginParamsDto.setIp(dto.getIp());
            equipmentLoginParamsDto.setPassword(dto.getPassword());
            equipmentLoginParamsDto.setPort(dto.getPort());
            equipmentLoginParamsDto.setUsername(dto.getUsername());
            this.login(equipmentLoginParamsDto);
        }
        cameraDriver = ConstantsFace.HK_CAMERA_DRIVER_MAP.get(dto.getIp());
        Integer ptzChannel = Optional.ofNullable(dto.getChannel()).orElse(cameraDriver.getStartChannel());

        HCNetSDK.NET_DVR_PTZABSOLUTEEX_CFG ptzAbsoluteExParam = this.getPTZAbsoluteExParam(cameraDriver.getIp(), cameraDriver.getUserId(), ptzChannel);
        HCNetSDK.NET_DVR_FOCUSMODE_CFG focusModeParam = this.getFocusModeParam(cameraDriver.getIp(), cameraDriver.getUserId(), ptzChannel);
        HCNetSDK.NET_DVR_AEMODECFG aeModeParam = this.getAeModeParam(cameraDriver.getIp(), cameraDriver.getUserId(), ptzChannel);
        HCNetSDK.NET_DVR_CAMERAPARAMCFG_EX ccdParam = this.getCCDParam(cameraDriver.getIp(), cameraDriver.getUserId(), ptzChannel);
        String tmp = this.getPtzInfoXml(cameraDriver.getIp(), cameraDriver.getUserId(), ptzChannel);
        String zoomFocus = this.getZoomFocus(cameraDriver.getIp(), cameraDriver.getUserId(), ptzChannel);
//        CameraParamDTO cameraParamDto = new CameraParamDTO();
        if (ptzAbsoluteExParam != null) {
            params.add(new EquipmentParamsDto("水平参数", "fPan", String.valueOf(ptzAbsoluteExParam.struPTZCtrl.fPan), ""));
            params.add(new EquipmentParamsDto("垂直参数", "fTilt", String.valueOf(ptzAbsoluteExParam.struPTZCtrl.fTilt), ""));
            params.add(new EquipmentParamsDto("变倍参数", "fZoom", String.valueOf(ptzAbsoluteExParam.struPTZCtrl.fZoom), ""));
            params.add(new EquipmentParamsDto("聚焦参数", "dwFocus", String.valueOf(ptzAbsoluteExParam.struPTZCtrl.dwFocus), ""));
            params.add(new EquipmentParamsDto("焦距范围", "dwFocalLen", String.valueOf(ptzAbsoluteExParam.dwFocalLen), ""));
            params.add(new EquipmentParamsDto("变倍配置类型", "byZoomType", String.valueOf(ptzAbsoluteExParam.byZoomType), ptzAbsoluteExParam.byZoomType==(byte)1 ? "焦距参数生效" : "变倍参数生效"));
        }
        if (focusModeParam != null) {
            params.add(new EquipmentParamsDto("聚焦模式", "byFocusMode", String.valueOf(focusModeParam.byFocusMode),
                    focusModeParam.byFocusMode== 0 ? "自动" : focusModeParam.byFocusMode ==(byte) 1 ? "手动" : "半自动"));
            params.add(new EquipmentParamsDto("聚焦值", "dwFocusPos", String.valueOf(focusModeParam.dwFocusPos), ""));
        }
        if (aeModeParam != null) {
            String ExposureModeSetValueFor = switch(aeModeParam.byExposureModeSet){
                case (byte)0 -> "手动模式";
                case (byte)1 ->"自动曝光";
                case (byte)2->"光圈优先";
                case (byte)3->"快门优先";
                case (byte)4->"增益优先";
                default -> "";
            };
            params.add(new EquipmentParamsDto("曝光模式", "byExposureModeSet", String.valueOf(aeModeParam.byExposureModeSet), ExposureModeSetValueFor));
            params.add(new EquipmentParamsDto("光圈", "iIrisSet", String.valueOf(aeModeParam.iIrisSet), ""));
            params.add(new EquipmentParamsDto("快球增益", "iGainSet", String.valueOf(aeModeParam.iGainSet), ""));
            params.add(new EquipmentParamsDto("增益限制", "iGainLimit", String.valueOf(aeModeParam.iGainLimit), ""));
            params.add(new EquipmentParamsDto("快门等级", "byShutterSet", String.valueOf(aeModeParam.byShutterSet), ""));
        }
        if (ccdParam != null) {
            //：0-室外，1-室内，2-默认，3-弱光
            params.add(new EquipmentParamsDto("场景模式", "bySceneMode", String.valueOf(ccdParam.bySceneMode),
                    ccdParam.bySceneMode == 0 ? "室外" : ccdParam.bySceneMode == 1 ? "室内" : ccdParam.bySceneMode == 2 ? "默认" : "弱光"
                    ));
        }
        if (StrUtil.isNotBlank(tmp)) {
            JSONObject jsonObject = XML.toJSONObject(tmp);
            params.add(new EquipmentParamsDto("云台变倍参数(透传协议)", "absoluteZoom", String.valueOf(
                    jsonObject.getJSONObject("PTZStatus").getJSONObject("AbsoluteHigh").getInt("absoluteZoom")), ""));
            params.add(new EquipmentParamsDto("云台水平参数(透传协议)", "azimuth", String.valueOf(
                    jsonObject.getJSONObject("PTZStatus").getJSONObject("AbsoluteHigh").getInt("azimuth")), "")
            );
            params.add(new EquipmentParamsDto("云台垂直参数(透传协议)", "elevation", String.valueOf(
                    jsonObject.getJSONObject("PTZStatus").getJSONObject("AbsoluteHigh").getInt("elevation")), ""));
        }
        if (StrUtil.isNotEmpty(zoomFocus)) {
            // 聚焦值
            JSONObject jsonObject = XML.toJSONObject(zoomFocus);
            params.add(new EquipmentParamsDto("机芯聚焦参数(透传协议)", "mnstFocus",
                    String.valueOf(jsonObject.getJSONObject("ZoomFocus").getInt("mnstFocus")), ""));
            params.add(new EquipmentParamsDto("机芯变倍参数(透传协议)", "pqrsZoom",
                    String.valueOf(jsonObject.getJSONObject("ZoomFocus").getInt("pqrsZoom")), ""));
        }
        return params;
    }



    private String getPtzInfoXml(String ip, Integer userId, Integer channel) {
        //一般取第一个通道的
        String requestUrL = HCNetSDK.GET_FOCUS_URL.replace("channelValue", String.valueOf(channel));//"GET /ISAPI/PTZCtrl/channels/"+channel+"/status";
        String ptzInfo = this.sendXmlConfig(ip, userId, requestUrL, "");
        log.debug("[HK]{},get ptz xml info:{}", ip, ptzInfo);
        return ptzInfo;
    }
    /**
     * 获取前端参数
     */
    private HCNetSDK.NET_DVR_CAMERAPARAMCFG_EX getCCDParam(String ip, Integer userId, Integer channel) {
        HCNetSDK.NET_DVR_CAMERAPARAMCFG_EX net_dvr_cameraparamcfg_ex = new HCNetSDK.NET_DVR_CAMERAPARAMCFG_EX();
        net_dvr_cameraparamcfg_ex.write();
        Pointer lpIpParaConfig = net_dvr_cameraparamcfg_ex.getPointer();
        IntByReference intByReference = new IntByReference();
        boolean ret = hcNetSDK.NET_DVR_GetDVRConfig(userId, HCNetSDK.NET_DVR_GET_CCDPARAMCFG_EX, channel, lpIpParaConfig, net_dvr_cameraparamcfg_ex.size(), intByReference);
        if (!ret) {
            log.error("[HK]{},channel:{},get CCD mode failed:{}", ip, channel, hcNetSDK.NET_DVR_GetLastError());
            return null;
        }
        net_dvr_cameraparamcfg_ex.read();
        return net_dvr_cameraparamcfg_ex;
    }

    private HCNetSDK.NET_DVR_AEMODECFG getAeModeParam(String ip, Integer userId, Integer channel) {
        //获取快球其他参数信息:曝光/增益
        HCNetSDK.NET_DVR_AEMODECFG net_dvr_aemodecfg = new HCNetSDK.NET_DVR_AEMODECFG();
        net_dvr_aemodecfg.write();
        IntByReference intByReference = new IntByReference();
        boolean ret = hcNetSDK.NET_DVR_GetDVRConfig(userId, HCNetSDK.NET_DVR_GET_AEMODECFG, channel, net_dvr_aemodecfg.getPointer(), net_dvr_aemodecfg.size(), intByReference);
        if (!ret) {
            log.error("[HK]{},channel:{},get ae mode failed:{}", ip, channel, hcNetSDK.NET_DVR_GetLastError());
            return null;
        }
        net_dvr_aemodecfg.read();
        return net_dvr_aemodecfg;
    }

    private HCNetSDK.NET_DVR_FOCUSMODE_CFG getFocusModeParam(String ip, Integer userId, Integer channel) {
        //获取快球聚焦模式信息
        HCNetSDK.NET_DVR_FOCUSMODE_CFG net_dvr_focusmode_cfg = new HCNetSDK.NET_DVR_FOCUSMODE_CFG();
        net_dvr_focusmode_cfg.write();
        IntByReference intByReference = new IntByReference();
        boolean ret = hcNetSDK.NET_DVR_GetDVRConfig(userId, HCNetSDK.NET_DVR_GET_FOCUSMODECFG, channel, net_dvr_focusmode_cfg.getPointer(), net_dvr_focusmode_cfg.size(), intByReference);
        if (!ret) {
            log.error("[HK]{},channel:{},get focus mode failed:{}", ip, channel, hcNetSDK.NET_DVR_GetLastError());
            return null;
        }
        net_dvr_focusmode_cfg.read();
        return net_dvr_focusmode_cfg;
    }

    /**
     * 获取高精度PTZ绝对位置配置
     */
    private HCNetSDK.NET_DVR_PTZABSOLUTEEX_CFG getPTZAbsoluteExParam(String ip, Integer userId, Integer channel) {
        HCNetSDK.NET_DVR_STD_CONFIG lpnet_dvr_std_config_get = new HCNetSDK.NET_DVR_STD_CONFIG();
        HCNetSDK.NET_PTZ_INFO net_ptz_info = new HCNetSDK.NET_PTZ_INFO();
        net_ptz_info.write();
        HCNetSDK.NET_DVR_PTZABSOLUTEEX_CFG net_dvr_ptzabsoluteex_cfg = new HCNetSDK.NET_DVR_PTZABSOLUTEEX_CFG();
        net_dvr_ptzabsoluteex_cfg.struPTZCtrl = net_ptz_info;
        net_dvr_ptzabsoluteex_cfg.write();
        Pointer pointer = new Memory(4);
        //通道号
        pointer.setInt(0, channel);
        lpnet_dvr_std_config_get.lpCondBuffer = pointer;
        lpnet_dvr_std_config_get.dwCondSize = 4;
        lpnet_dvr_std_config_get.lpOutBuffer = net_dvr_ptzabsoluteex_cfg.getPointer();
        lpnet_dvr_std_config_get.dwOutSize = net_dvr_ptzabsoluteex_cfg.size();
        lpnet_dvr_std_config_get.byDataType = 0;
        lpnet_dvr_std_config_get.write();
        boolean ret = hcNetSDK.NET_DVR_GetSTDConfig(userId, HCNetSDK.NET_DVR_GET_PTZABSOLUTEEX, lpnet_dvr_std_config_get);
        if (!ret) {
            log.error("[HK]{},channel:{},get ptz absolute param failed:{}", ip, channel, hcNetSDK.NET_DVR_GetLastError());
            return null;
        }
        lpnet_dvr_std_config_get.read();
        net_dvr_ptzabsoluteex_cfg.read();
        net_ptz_info.read();
        log.info("[HK]{},channel:{},get ptz absolute param success", ip, channel);
        return net_dvr_ptzabsoluteex_cfg;
    }

    @Override
    public boolean motionAbort(QueryParamsDto dto) {
        //急停以及重置标志位
        this.setStopFlag();
        return true;
    }

    private void setStopFlag(){
        stopCache.put("stop",true);
    }

    private boolean getStopFlag(){
        //不存在的话就返回false
        return Optional.ofNullable(stopCache.getIfPresent("stop")).orElse(false);
    }

    public Table<String, String, String> getParamsCacheTable(){
        return  paramsCacheTable;
    }


}
